﻿//////////////////////////////////
//  C# 也可以做Sniffer
//  SharpSniffer
////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using WawaSoft.WawaCapturer;
using System.Runtime.InteropServices;

namespace SharpSniffer
{

    class Program
    {
        static readonly string ConsoleSperator = new string('-', Console.BufferWidth - 1);
        static readonly string ConsoleSperatorHf = new string('-', Console.BufferWidth / 2 - 1);
        static int prot = 1234;

        static List<PacketArrivedEventArgs> pakcList = new List<PacketArrivedEventArgs>() { 
         new PacketArrivedEventArgs(),
         new PacketArrivedEventArgs(),
        };

        static Queue<PacketArrivedEventArgs> pool = new Queue<PacketArrivedEventArgs>(2);

        static void Main(string[] args)
        {


                Console.WriteLine("1手动抓;2自动");
                string str = Console.ReadLine();
                if (str == "1")
                {
                    Capture cap = new CaptureOTA();
                    cap.Start();
 
                    while (true)
                    {
                        Console.ReadLine();
                    }
                }

                if (str == "2")
                {


                    StartCapture();
                    while (true)
                    {
                        Console.ReadLine();
                    }
                }
            

        }


        /// <summary>
        /// 开始抓包
        /// </summary>
        static void StartCapture()
        {
            //初始化池
            pakcList.ForEach((p) => { pool.Enqueue(p); });


            try
            {
                //创建socket
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
                //可以帮定同一端口
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                //端口重用
                // socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                socket.ExclusiveAddressUse = false;
                //socket.Blocking = false;

                byte[] buf = new byte[65535 * 2];   //缓存大一点没关系，小了可能一次放不下

                using (socket)
                {
                    PrintLine("socket created!");

                    //绑定到本机，端口可以任意
                    var localHostIpAddress = GetHostAdress();
                    Console.WriteLine("trying to bind to local IP: {0}", localHostIpAddress);
                    //socket.Bind(new IPEndPoint(localHostIpAddress, 0));
                    socket.Bind(new IPEndPoint(localHostIpAddress, prot));
                    PrintLine("binded to [" + socket.LocalEndPoint + "]");

                    byte[] outValue = BitConverter.GetBytes(0);
                    byte[] inValue = BitConverter.GetBytes(1);
                    socket.IOControl(IOControlCode.ReceiveAll, inValue, null);   //对IO设置为可以接受所有包
                    PrintLine("IOControl seted!");

                    PrintLine("Sniffer begined.");


                    int index = 0;  //表示当前是第几个包（1based）

                    while (true)
                    {
                        index++;
                        try
                        {


                            // ipep.Address = IPAddress.Any;   //从任何地方接收数据
                            // ipep.Port = 0;


                            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0); //从任何地方接收数据
                            EndPoint ep = (EndPoint)ipep;


                            int recvedSize = socket.ReceiveFrom(buf, SocketFlags.None, ref ep);   //用ReceiveFrom接受数据
                            //        socket.Receive(buf);             //用Receive也能接受到数据，不过使用ReceiveFrom可以直接获取发送方IP地址



                            OperData(buf, recvedSize, index);


                            //接收到包了，打印出来
                            //Print('[');
                            //Print(GetCurrentTimeString());
                            //PrintLine("] Received [" + recvedSize+"] bytes from ["+ep.ToString()+"]");
                            ////string s = Encoding.ASCII.GetString(buf, 0, recvedSize);
                            ////Print("ASCII: \n");
                            ////PrintLine(s);
                            //string s = GetByteArrayHexString(buf, 0, recvedSize);
                            //Print("Hex: \n");
                            //PrintLine(s);

                        }
                        catch (Exception ex)
                        {
                            if (ex is SocketException)
                            {
                                var sex = (SocketException)ex;
                                Console.WriteLine("SocketErrorCode=" + sex.ErrorCode);
                            }
                            Console.WriteLine(ex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex is SocketException)
                {
                    var sex = (SocketException)ex;
                    Console.WriteLine("SocketErrorCode=" + sex.ErrorCode);
                }
                Console.WriteLine(ex);
            }
            Console.ReadKey();

        }



        /// <summary>
        /// TCP包
        /// </summary>
        static TCPPackeg tcpPack = new TCPPackeg();
        /// <summary>
        /// UDP包
        /// </summary>
        static UDPPackeg udpPack = new UDPPackeg();

        /// <summary>
        /// IP包
        /// </summary>
        static IPHead ipPack = new IPHead();

        static System.DateTime Printdt = DateTime.Now;

        /// <summary>
        /// 处理数据
        /// </summary>
        /// <param name="arr"></param>
        static void OperData(byte[] buf, int recvedSize, long index)
        {

            //取池
            PacketArrivedEventArgs pakobj = pool.Count > 0 ? pool.Dequeue() : new PacketArrivedEventArgs();
            //处理数据
            var d = Receive(buf, recvedSize);

            //处理IP头
            IPHead iph = IPHead.Parse(buf, ipPack);

            if ((d.Protocol == "TCP" || d.Protocol == "UDP")
                //判断 端口过滤
                //&& (d.DestinationPort == 8011||d.OriginationPort==8011)
                //&& d.MessageBuffer.Length > 20
                )
            {
                Print(ConsoleSperatorHf);
                Print(index);
                PrintLine(ConsoleSperatorHf);
                Console.WriteLine(d);

                if (ipPack.protocol == ProtocolType.Udp)
                {
                    UDPPackeg packUdp = UDPPackeg.Parse(buf, ipPack.ihl * 4, udpPack);


                    //过滤
                    int datalen = d.MessageBuffer.Length - 8;
                    if (datalen > 0)
                    {
                        byte[] data = new byte[datalen];
                        Buffer.BlockCopy(d.MessageBuffer, 8, data, 0, datalen);

                        //200 毫秒打印一次
                        if (Printdt < DateTime.Now)
                        {

                            Console.WriteLine(string.Format("Pack{0}收到数据:{1};{2}", d.Protocol,
        System.Text.Encoding.GetEncoding("GB2312").GetString(data, 0, data.Length > 30 ? 30 : data.Length)
        , BitConverter.ToString(data, 0, data.Length > 30 ? 30 : data.Length).Replace("-", string.Empty)));

                            Printdt = DateTime.Now.AddMilliseconds(200);
                        }


                    }


                }

                if (ipPack.protocol == ProtocolType.Tcp)
                {
                    TCPPackeg packTcp = TCPPackeg.Parse(buf, ipPack.ihl * 4, recvedSize, tcpPack);
                    if (packTcp.TCPData.Length > 0)
                    {
                        var den = d.MessageBuffer.Length - 20 == packTcp.TCPData.Length;
                        if (!den)
                        {

                            //200 毫秒打印一次
                            if (Printdt < DateTime.Now)
                            {
                                Console.WriteLine(string.Format("Pack{0}收到数据:{1};{2}", d.Protocol,
                                 System.Text.Encoding.GetEncoding("GB2312").GetString(packTcp.TCPData, 0, packTcp.TCPData.Length > 30 ? 30 : packTcp.TCPData.Length)
                                 , BitConverter.ToString(packTcp.TCPData, 0, packTcp.TCPData.Length > 30 ? 30 : packTcp.TCPData.Length).Replace("-", string.Empty)));

                                Printdt = DateTime.Now.AddMilliseconds(200);
                            }
                        }
                    }

                    #region 以前的打印,TCP包头判断有问题
                    //过滤
                    //int datalen = d.MessageBuffer.Length - 20;
                    int datalen = d.MessageBuffer.Length - packTcp.thl * 4;
                    if (datalen > 0)
                    {
                        byte[] data = new byte[datalen];
                        //Buffer.BlockCopy(d.MessageBuffer, 20, data, 0, datalen);
                        Buffer.BlockCopy(d.MessageBuffer, packTcp.thl * 4, data, 0, datalen);

                        //200 毫秒打印一次
                        if (Printdt < DateTime.Now)
                        {

                            Console.WriteLine(string.Format("Pack{0}收到数据:{1};{2}", d.Protocol,
                                System.Text.Encoding.GetEncoding("GB2312").GetString(data, 0, data.Length > 30 ? 30 : data.Length)
                                , BitConverter.ToString(data, 0, data.Length > 30 ? 30 : data.Length).Replace("-", string.Empty)));

                            Printdt = DateTime.Now.AddMilliseconds(200);
                        }
                    }

                    #endregion
                }




            }



            //还池
            pool.Enqueue(pakobj);
        }

        //获取本机IP地址
        private static IPAddress GetHostAdress()
        {
            string hostName = Dns.GetHostName();
            var hostAddreses = Dns.GetHostAddresses(hostName);
            List<IPAddress> addressList = new List<IPAddress>(hostAddreses.Length);
            foreach (var item in hostAddreses)
            {
                if (item.AddressFamily == AddressFamily.InterNetwork)
                {
                    addressList.Add(item);
                }
            }
            if (addressList.Count != 0)
            {
                if (addressList.Count == 1)
                {
                    return addressList[0];
                }
                else
                {
                    Console.WriteLine("请选择要绑定到的本机IP地址(IPv4)：");
                    for (int i = 0; i < addressList.Count; i++)
                    {
                        var addr = addressList[i];
                        Console.WriteLine("\t{0}: {1}", i, addr);
                    }
                    int sel = int.Parse(Console.ReadLine());
                    return addressList[sel];
                }
            }
            else
            {
                Console.Write("请输入本机IP地址：");
                string s = Console.ReadLine();
                return IPAddress.Parse(s);
            }
        }
        //获取表示当前时间的字符串
        private static string GetCurrentTimeString()
        {
            DateTime now = DateTime.Now;
            return now.Hour + ":" + now.Minute + ":" + now.Second + "." + now.Millisecond;
        }
        const string HexValues = "0123456789ABCDEF";
        //把字节数组转换为十六进制表示的字符串
        private static string GetByteArrayHexString(byte[] buf, int startIndex, int size)
        {
            StringBuilder sb = new StringBuilder(size * 5);
            sb.AppendFormat("{0,3:X}: ", 0);
            int j = 1;
            for (int i = startIndex, n = startIndex + size; i < n; i++, j++)
            {
                byte b = buf[i];
                char c = HexValues[(b & 0x0f0) >> 4];
                sb.Append(c);
                c = HexValues[(b & 0x0f)];
                sb.Append(c);
                sb.Append(' ');
                if ((j & 0x0f) == 0)
                {
                    sb.Append(' ');
                    //sb.Append(Encoding.ASCII.GetString(buf,i-15,8));
                    AppendPrintableBytes(sb, buf, i - 15, 8);
                    sb.Append(' ');
                    //sb.Append(Encoding.ASCII.GetString(buf, i - 7, 8));
                    AppendPrintableBytes(sb, buf, i - 7, 8);
                    if (i + 1 != n)
                    {
                        sb.Append('\n');
                        sb.AppendFormat("{0,3:X}: ", i - 1);    //偏移
                    }
                }
                else if ((j & 0x07) == 0)
                {
                    sb.Append(' ');
                }
            }
            int t;
            if ((t = ((j - 1) & 0x0f)) != 0)
            {
                for (int k = 0, kn = 16 - t; k < kn; k++)
                {
                    sb.Append("   ");
                }
                if (t <= 8)
                {
                    sb.Append(' ');
                }

                sb.Append(' ');
                //   sb.Append(Encoding.ASCII.GetString(buf, startIndex + size - t, t>8?8:t));
                AppendPrintableBytes(sb, buf, startIndex + size - t, t > 8 ? 8 : t);
                if (t > 8)
                {
                    sb.Append(' ');
                    //   sb.Append(Encoding.ASCII.GetString(buf, startIndex + size - t + 8, t - 8));
                    AppendPrintableBytes(sb, buf, startIndex + size - t + 8, t - 8);
                }
            }
            return sb.ToString();
        }
        //向sb中添加buf中可打印字符，不可打印字符用'.'代替
        private static void AppendPrintableBytes(StringBuilder sb, byte[] buf, int startIndex, int len)
        {
            for (int i = startIndex, n = startIndex + len; i < n; i++)
            {
                char c = (char)buf[i];
                if (!char.IsControl(c))
                {
                    sb.Append(c);
                }
                else
                {
                    sb.Append('.');
                }
            }
        }
        //打印t+'\n'
        static void PrintLine<T>(T t)
        {
            Console.WriteLine(t);
        }
        //打印t
        static void Print<T>(T t)
        {
            Console.Write(t);
        }


        //解析接收的数据包，形成PacketArrivedEventArgs事件数据类对象，并引发PacketArrival事件
        unsafe public static PacketArrivedEventArgs Receive(byte[] buf, int len, PacketArrivedEventArgs refobj = null)
        {
            byte temp_protocol = 0;
            uint temp_version = 0;
            uint temp_ip_srcaddr = 0;
            uint temp_ip_destaddr = 0;
            short temp_srcport = 0;
            short temp_dstport = 0;
            IPAddress temp_ip;

            PacketArrivedEventArgs e = refobj;
            if (refobj == null)
            {
                e = new PacketArrivedEventArgs();//新网络数据包信息事件
            }

            fixed (byte* fixed_buf = buf)
            {
                IPHeader* head = (IPHeader*)fixed_buf;//把数据流整和为IPHeader结构
                e.HeaderLength = (uint)(head->ip_verlen & 0x0F) << 2;
                e.IPHeaderBuffer = new byte[e.HeaderLength];

                temp_protocol = head->ip_protocol;
                switch (temp_protocol)//提取协议类型
                {
                    case 1: e.Protocol = "ICMP"; break;
                    case 2: e.Protocol = "IGMP"; break;
                    case 6: e.Protocol = "TCP"; break;
                    case 17: e.Protocol = "UDP"; break;
                    default: e.Protocol = "UNKNOWN"; break;
                }

                temp_version = (uint)(head->ip_verlen & 0xF0) >> 4;//提取IP协议版本
                e.IPVersion = temp_version.ToString();

                //以下语句提取出了PacketArrivedEventArgs对象中的其他参数
                temp_ip_srcaddr = head->ip_srcaddr;
                temp_ip_destaddr = head->ip_destaddr;
                temp_ip = new IPAddress(temp_ip_srcaddr);
                e.OriginationAddress = temp_ip.ToString();
                temp_ip = new IPAddress(temp_ip_destaddr);
                e.DestinationAddress = temp_ip.ToString();

                temp_srcport = *(short*)&fixed_buf[e.HeaderLength];
                temp_dstport = *(short*)&fixed_buf[e.HeaderLength + 2];
                // e.OriginationPort = IPAddress.NetworkToHostOrder(temp_srcport).ToString();
                //e.DestinationPort = IPAddress.NetworkToHostOrder(temp_dstport).ToString();
                e.OriginationPort = BitConverter.ToUInt16(BitConverter.GetBytes(temp_srcport).Reverse().ToArray(), 0);
                e.DestinationPort = BitConverter.ToUInt16(BitConverter.GetBytes(temp_dstport).Reverse().ToArray(), 0);


                e.PacketLength = (uint)len;
                e.MessageLength = (uint)len - e.HeaderLength;
                e.MessageBuffer = new byte[e.MessageLength];

                e.ReceiveBuffer = buf;
                //把buf中的IP头赋给PacketArrivedEventArgs中的IPHeaderBuffer
                Array.Copy(buf, 0, e.IPHeaderBuffer, 0, (int)e.HeaderLength);
                //把buf中的包中内容赋给PacketArrivedEventArgs中的MessageBuffer
                Array.Copy(buf, (int)e.HeaderLength, e.MessageBuffer, 0, (int)e.MessageLength);
            }

            return e;
            //引发PacketArrival事件
            // OnPacketArrival(e);
        }



        //将Byte转换为结构体类型
        public static byte[] StructToBytes(object structObj, int size = 20)
        {
            IPHeader sd;
            int num = 2;
            byte[] bytes = new byte[size];
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将结构体拷到分配好的内存空间
            Marshal.StructureToPtr(structObj, structPtr, false);
            //从内存空间拷贝到byte 数组
            Marshal.Copy(structPtr, bytes, 0, size);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            return bytes;

        }

        //将Byte转换为结构体类型
        public static object ByteToStruct(byte[] bytes, Type type)
        {
            int size = Marshal.SizeOf(type);
            if (size > bytes.Length)
            {
                return null;
            }
            //分配结构体内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将byte数组拷贝到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //将内存空间转换为目标结构体
            object obj = Marshal.PtrToStructure(structPtr, type);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            return obj;
        }
    }
}
